//! A collection of fundamental and/or simple types used by other modules

use crate::error::{err, Result, StatusCode};

use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;

use bytes::Bytes;

/// 定义了 LSM 树中层级的总数为 7 层。在 LSM 树中，数据会从内存表（MemTable）逐步合并到不同层级的磁盘 SSTable 中，层级越高，数据量通常越大，访问频率越低。
/// 7 层是 LevelDB/RocksDB 等经典实现的常见配置，用于平衡写入性能和读取效率。
pub const NUM_LEVELS: usize = 7;

/// Represents a sequence number of a single entry.
///
/// 定义了 SequenceNumber 类型（序列编号），本质是 u64（64 位无符号整数）。序列编号是 LSM 树中用于区分同一键的不同版本的关键标识：
///
/// 每次写入操作（新增、修改、删除）都会分配一个全局递增的序列编号。
///
/// 即使是同一个键，不同的序列编号也代表不同的版本（通常编号越大版本越新）。
pub type SequenceNumber = u64;

/// 定义了序列编号的最大值（2^56 - 1）。限制为 56 位而非 64 位，是因为 LSM 树中通常会将序列编号与键的类型（如删除标记）共用一个 64 位整数（例如用高 8 位表示操作类型，低 56 位表示序列编号），
/// 这样可以在一个整数中同时存储键的版本和操作信息，优化存储效率。
pub const MAX_SEQUENCE_NUMBER: SequenceNumber = (1 << 56) - 1;

/// A shared thingy with interior mutability.
pub type Shared<T> = Rc<RefCell<T>>;

pub fn share<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

/// 这段代码定义了一个名为 Direction 的公共枚举类型，用于表示方向，包含两个变体：Forward（向前）和 Reverse（向后）。
///
/// #[derive(PartialEq)] 派生宏为该枚举自动实现了 PartialEq 特性，使其能够进行相等性比较（如 direction == Direction::Forward）
///
/// 枚举的两个变体 Forward 和 Reverse 分别代表两种相反的方向
#[derive(PartialEq)]
pub enum Direction {
    Forward,
    Reverse,
}

/// Denotes a key range
///
/// 这段代码定义了一个名为 Range 的结构体，用于表示字节数组（通常是键）的范围区间，在存储引擎中主要用于描述键的取值范围，方便进行范围查询或数据分片操作。
///
/// Range 是一个泛型结构体，带有生命周期参数 'a，表示它引用的字节数组（start 和 limit）的生命周期。
pub struct Range<'a> {
    /// 范围的起始点（包含该值），通常是一个键的字节表示。
    pub start: &'a [u8],
    /// 范围的结束点（通常是排他的，即不包含该值），也是另一个键的字节表示。
    pub limit: &'a [u8],
}

/// An extension of the standard `Iterator` trait that supports some methods necessary for LevelDB.
/// This works because the iterators used are stateful and keep the last returned element.
///
/// Note: Implementing types are expected to hold `!valid()` before the first call to `advance()`.
///
/// test_util::test_iterator_properties() verifies that all properties hold.
pub trait LdbIterator {
    /// Advances the position of the iterator by one element (which can be retrieved using
    /// current(). If no more elements are available, advance() returns false, and the iterator
    /// becomes invalid (i.e. as if reset() had been called).
    fn advance(&mut self) -> bool;
    /// Return the current item (i.e. the item most recently returned by `next()`).
    fn current(&self) -> Option<(Bytes, Bytes)>;
    /// Seek the iterator to `key` or the next bigger key. If the seek is invalid (past last
    /// element, or before first element), the iterator is `reset()` and not valid.
    fn seek(&mut self, key: &[u8]);
    /// Resets the iterator to be `!valid()`, i.e. positioned before the first element.
    fn reset(&mut self);
    /// Returns true if the iterator is not positioned before the first or after the last element,
    /// i.e. if `current()` would succeed.
    fn valid(&self) -> bool;
    /// Go to the previous item; if the iterator is moved beyond the first element, `prev()`
    /// returns false and it will be `!valid()`. This is inefficient for most iterator
    /// implementations.
    fn prev(&mut self) -> bool;

    // default implementations.

    /// next is like Iterator::next(). It's implemented here because Rust disallows implementing a
    /// foreign trait for any type, thus we can't do `impl<T: LdbIterator> Iterator<Item=Vec<u8>>
    /// for T {}`.
    fn next(&mut self) -> Option<(Vec<u8>, Vec<u8>)> {
        if !self.advance() {
            return None;
        }
        if let Some((key, val)) = self.current() {
            Some((key.to_vec(), val.to_vec()))
        } else {
            None
        }
    }

    /// seek_to_first seeks to the first element.
    fn seek_to_first(&mut self) {
        self.reset();
        self.advance();
    }
}

/// current_key_val is a helper allocating two vectors and filling them with the current key/value
/// of the specified iterator.
///
/// 在seek方法中已经找到了数据, 并且已经修改了current指针, 现在从跳表中知直接解析索引块, 拿到数据就可以了
pub fn current_key_val<It: LdbIterator + ?Sized>(it: &It) -> Option<(Vec<u8>, Vec<u8>)> {
    if let Some((k, v)) = it.current() {
        Some((k.to_vec(), v.to_vec()))
    } else {
        None
    }
}

impl LdbIterator for Box<dyn LdbIterator> {
    fn advance(&mut self) -> bool {
        self.as_mut().advance()
    }
    fn current(&self) -> Option<(Bytes, Bytes)> {
        self.as_ref().current()
    }
    fn seek(&mut self, key: &[u8]) {
        self.as_mut().seek(key)
    }
    fn reset(&mut self) {
        self.as_mut().reset()
    }
    fn valid(&self) -> bool {
        self.as_ref().valid()
    }
    fn prev(&mut self) -> bool {
        self.as_mut().prev()
    }
}

/// The unique (sequential) number of a file.
pub type FileNum = u64;

/// Describes a file on disk.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FileMetaData {
    // default: size / 16384.
    pub allowed_seeks: usize,
    pub num: FileNum,
    pub size: usize,
    // these are in InternalKey format:
    pub smallest: Bytes,
    pub largest: Bytes,
}

/// 文件类型
#[derive(Debug, Clone, PartialEq)]
pub enum FileType {
    /// 通常指事务日志文件，用于记录操作日志，可能用于崩溃恢复
    Log,
    /// 数据库锁文件，用于确保同一时间只有一个进程访问数据库
    DBLock,
    ///  数据表格文件，存储实际的键值对数据（可能是 SSTable 格式）
    Table,
    /// 描述符文件，可能包含数据库元信息或文件列表
    Descriptor,
    /// 当前文件，通常指向当前有效的描述符文件
    Current,
    /// 临时文件，用于数据库操作过程中的临时存储
    Temp,
    /// 信息日志文件，记录数据库运行过程中的信息、警告或错误
    InfoLog,
}

/// 通过文件路径判断文件是什么类型
/// 返回 (文件编号, 文件类型)
pub fn parse_file_name<P: AsRef<Path>>(ff: P) -> Result<(FileNum, FileType)> {
    let f = ff.as_ref().to_str().unwrap();
    if f == "CURRENT" {
        return Ok((0, FileType::Current));
    } else if f == "LOCK" {
        return Ok((0, FileType::DBLock));
    } else if f == "LOG" || f == "LOG.old" {
        return Ok((0, FileType::InfoLog));
    } else if f.starts_with("MANIFEST-") {
        if let Some(ix) = f.find('-') {
            if let Ok(num) = FileNum::from_str_radix(&f[ix + 1..], 10) {
                return Ok((num, FileType::Descriptor));
            }
            return err(StatusCode::InvalidArgument, "manifest file number is invalid", );
        }
        return err(StatusCode::InvalidArgument, "manifest file has no dash");
    } else if let Some(ix) = f.find('.') {
        // 00012345.log 00123.sst ...
        if let Ok(num) = FileNum::from_str_radix(&f[0..ix], 10) {
            let typ = match &f[ix + 1..] {
                "log" => FileType::Log,
                "sst" | "ldb" => FileType::Table,
                "dbtmp" => FileType::Temp,
                _ => {
                    return err(StatusCode::InvalidArgument, "unknown numbered file extension", )
                }
            };
            return Ok((num, typ));
        }
        return err(StatusCode::InvalidArgument, "invalid file number for table or temp file", );
    }
    err(StatusCode::InvalidArgument, "unknown file type")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_types_parse_file_name() {
        for c in &[
            ("CURRENT", (0, FileType::Current)),
            ("LOCK", (0, FileType::DBLock)),
            ("LOG", (0, FileType::InfoLog)),
            ("LOG.old", (0, FileType::InfoLog)),
            ("MANIFEST-01234", (1234, FileType::Descriptor)),
            ("001122.sst", (1122, FileType::Table)),
            ("001122.ldb", (1122, FileType::Table)),
            ("001122.dbtmp", (1122, FileType::Temp)),
        ] {
            assert_eq!(parse_file_name(c.0).unwrap(), c.1);
        }
        assert!(parse_file_name("xyz.LOCK").is_err());
        assert!(parse_file_name("01a.sst").is_err());
        assert!(parse_file_name("0011.abc").is_err());
        assert!(parse_file_name("MANIFEST-trolol").is_err());
    }
}
